﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using XiaoSe.IO.Compression;

namespace XiaoSe.Web
{
    public class HttpResponse
    {
        #region 私有字段

        /// <summary>
        /// 响应头数据
        /// </summary>
        private byte[] _responseHeaderData;

        /// <summary>
        /// 响应体数据，返回是文本有效
        /// </summary>
        private byte[] _responseBodyData;

        /// <summary>
        /// 序列化后的响应头
        /// </summary>
        private Dictionary<string, string> _responseHeader;

        /// <summary>
        /// 响应体字符
        /// </summary>
        private string _responseBody;

        /// <summary>
        /// http协议版本
        /// </summary>
        private string _httpVersion;

        /// <summary>
        /// 响应码
        /// </summary>
        private int _responseCode;

        private string _responseCodeText;

        /// <summary>
        /// 二进制文件路径
        /// </summary>
        private string _byteDataFilePath;

        private bool _isBinaryFile = true;

        /// <summary>
        /// 二进制文件流
        /// </summary>
        private FileStream _fileStream;

        /// <summary>
        /// 返回是文本数据内存
        /// </summary>
        private MemoryStream txtStream;

        /// <summary>
        /// 是否找到响应头
        /// </summary>
        private bool _isFindHead;

        /// <summary>
        /// 响应头编码
        /// </summary>
        private Encoding _responseHeaderEncode = Encoding.UTF8;

        /// <summary>
        /// 响应体编码
        /// </summary>
        private Encoding _responseBodyEncode;

        #endregion

        #region 属性

        private readonly string RequestUrl;

        /// <summary>
        /// 响应头数据
        /// </summary>
        public byte[] ResponseHeaderData
        {
            get { return _responseHeaderData; }
        }

        /// <summary>
        /// 响应体数据，返回是文本有效
        /// </summary>
        public byte[] ResponseBodyData
        {
            get { return _responseBodyData; }
        }

        /// <summary>
        /// 响应头键值对，用小写字母表示
        /// </summary>
        public Dictionary<string, string> ResponseHeader
        {
            get { return _responseHeader; }
        }

        /// <summary>
        /// HTTP协议版本
        /// </summary>
        public string HttpVersion
        {
            get { return _httpVersion; }
        }

        /// <summary>
        /// 响应码
        /// </summary>
        public int ResponseCode
        {
            get { return _responseCode; }
        }

        /// <summary>
        /// 响应码状态字符串
        /// </summary>
        public string ResponseCodeText
        {
            get { return _responseCodeText; }
        }

        /// <summary>
        /// 解析响应头编码
        /// </summary>
        public Encoding ResponseHeaderEncode
        {
            get { return _responseHeaderEncode; }
            set { _responseHeaderEncode = value; }
        }

        /// <summary>
        /// 解析响应体编码
        /// </summary>
        public Encoding ResponseBodyEncode
        {
            get { return _responseBodyEncode; }
            set { _responseBodyEncode = value; }
        }

        /// <summary>
        /// 二进制文件保存路径
        /// </summary>
        public string ByteDataFilePath
        {
            get { return _byteDataFilePath; }
            set { _byteDataFilePath = value; }
        }

        /// <summary>
        /// 是否是二进制文件
        /// </summary>
        public bool IsBinaryFile
        {
            get { return _isBinaryFile; }
        }

        #endregion


        #region 构造函数

        /// <summary>
        /// 
        /// </summary>
        /// <param name="requestUrl"></param>
        /// <param name="buffer">从中读取数据的缓冲区</param>
        public HttpResponse(string requestUrl, byte[] buffer)
        {
            RequestUrl = requestUrl;
            SearchRequestHeader(buffer);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="requestUrl"></param>
        /// <param name="buffer">从中读取数据的缓冲区</param>
        /// <param name="offset">buffer 中的字节偏移量，从此处开始读取</param>
        /// <param name="count">最多读取的字节数</param>
        public HttpResponse(string requestUrl, byte[] buffer, int offset, int count)
        {
            RequestUrl = requestUrl;
            AppendData(buffer, offset, count);
        }

        #endregion

        /// <summary>
        /// 追加数据
        /// </summary>
        /// <param name="buffer">从中读取数据的缓冲区</param>
        /// <param name="offset">buffer 中的字节偏移量，从此处开始读取</param>
        /// <param name="count">最多读取的字节数</param>
        public void AppendData(byte[] buffer, int offset, int count)
        {
            byte[] bufferNew = new byte[count];
            Array.Copy(buffer, offset, bufferNew, 0, count);
            SearchRequestHeader(bufferNew);
        }


        /// <summary>
        /// 查找响应头
        /// </summary>
        /// <param name="buffer"></param>
        private void SearchRequestHeader(byte[] buffer)
        {
            if (!_isFindHead)//还没找到响应头
            {
                //响应头结束位置
                int iResponseHeaderEndPosition = 0;
                for (int i = 0; i < buffer.Length; i++)
                {
                    if (buffer[i] != '\r' || (i + 3) >= buffer.Length) continue;
                    if (buffer[i + 1] != '\n' || buffer[i + 2] != '\r' || buffer[i + 3] != '\n') continue;
                    iResponseHeaderEndPosition = i + 3;
                    _isFindHead = true;
                    break;
                }
                if (!_isFindHead)//如果还没找到响应头
                {
                    iResponseHeaderEndPosition = buffer.Length - 1;
                }
                if (_responseHeaderData != null)
                {
                    MemoryStream stream = new MemoryStream(_responseHeaderData);
                    stream.Write(buffer, 0, iResponseHeaderEndPosition + 1);
                    _responseHeaderData = stream.ToArray();
                    stream.Close();
                }
                else
                {
                    _responseHeaderData = new byte[iResponseHeaderEndPosition + 1];
                    Array.Copy(buffer, 0, _responseHeaderData, 0, iResponseHeaderEndPosition + 1);
                }
                if (!_isFindHead || _responseHeader != null) return;
                string strHeader = _responseHeaderEncode.GetString(_responseHeaderData);
                string[] arrayHeader = strHeader.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                if (arrayHeader.Length > 0)
                {
                    _responseHeader = new Dictionary<string, string>();
                    for (int i = 0; i < arrayHeader.Length; i++)
                    {
                        if (i == 0)//响应状态
                        {
                            string[] arrayState = arrayHeader[i].Split(' ');
                            if (arrayState.Length == 0)
                            {
                                break;
                            }
                            if (arrayState.Length >= 1)
                            {
                                _httpVersion = arrayState[0];
                            }
                            if (arrayState.Length >= 2)
                            {
                                _responseCode = int.Parse(arrayState[1]);
                            }
                            if (arrayState.Length >= 3)
                            {
                                _responseCodeText = arrayState[2];
                            }
                        }
                        else//响应头
                        {
                            string[] arrayHeaderInfo = arrayHeader[i].Split(new[] { ": " }, StringSplitOptions.RemoveEmptyEntries);
                            if (!_responseHeader.ContainsKey(arrayHeaderInfo[0].Trim().ToLower()))
                            {
                                _responseHeader.Add(arrayHeaderInfo[0].Trim().ToLower(), arrayHeaderInfo[1].Trim().ToLower());
                            }
                        }
                    }
                }
                if ((iResponseHeaderEndPosition + 1) >= buffer.Length) return;
                byte[] temp = new byte[buffer.Length - iResponseHeaderEndPosition - 1];
                Array.Copy(buffer, iResponseHeaderEndPosition + 1, temp, 0, buffer.Length - iResponseHeaderEndPosition - 1);
                WriteResponseBody(temp);
            }
            else
            {
                WriteResponseBody(buffer);
            }
        }

        /// <summary>
        /// 写响应体数据
        /// </summary>
        /// <param name="buffer"></param>
        private void WriteResponseBody(byte[] buffer)
        {
            if (_isBinaryFile && string.IsNullOrEmpty(_byteDataFilePath))//初始判断，判断是否是二进制文件
            {
                string strContentType = null;
                if (_responseHeader.ContainsKey("content-type"))
                {
                    strContentType = _responseHeader["content-type"];
                }
                if (!string.IsNullOrEmpty(strContentType) && !strContentType.Contains("text"))
                {
                    _isBinaryFile = true;
                    string fileName = null;
                    if (_responseHeader.ContainsKey("content-disposition"))
                    {
                        string strDisposition = _responseHeader["content-disposition"];
                        if (!string.IsNullOrEmpty(strDisposition))
                        {
                            int index = strDisposition.IndexOf('=');
                            if (index > -1)
                            {
                                fileName = strDisposition.Substring(index + 1);
                            }
                        }
                    }
                    if (string.IsNullOrEmpty(fileName))
                    {
                        int index = RequestUrl.LastIndexOf("/", StringComparison.Ordinal);
                        if (index > -1)
                        {
                            fileName = RequestUrl.Substring(index + 1);
                        }
                    }
                    if (string.IsNullOrEmpty(fileName))
                    {
                        fileName = Guid.NewGuid() + ".xs";
                    }
                    //_byteDataFilePath = Environment.GetEnvironmentVariable("Temp") + "\\" + fileName;
                    _byteDataFilePath = "E:\\" + fileName;
                }
                else
                {
                    _isBinaryFile = false;
                }
            }
            if (_isBinaryFile && !string.IsNullOrEmpty(_byteDataFilePath))//如果是二进制文件
            {
                _isBinaryFile = true;
                if (_fileStream == null)
                {
                    _fileStream = new FileStream(_byteDataFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite,
                        FileShare.ReadWrite);
                    _fileStream.Seek(0, SeekOrigin.End);
                }
                _fileStream.Write(buffer, 0, buffer.Length);
                _fileStream.Flush();
                return;
            }

            if (txtStream == null)
            {
                txtStream = new MemoryStream();
            }
            txtStream.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// 获取响应Html，如果是二进制文件则返回null
        /// </summary>
        /// <returns></returns>
        public string GetResponseHtml()
        {
            if (_isBinaryFile)
            {
                return null;
            }
            if (!string.IsNullOrEmpty(_responseBody))
            {
                return _responseBody;
            }
            string strContentType = null;
            if (_responseHeader.ContainsKey("content-encoding"))
            {
                strContentType = _responseHeader["content-encoding"];
            }
            if (_responseHeader.ContainsKey("transfer-encoding") && _responseHeader["transfer-encoding"] == "chunked")
            {
                _responseBodyData = LinkDataByTransferEncoding(_responseBodyData);
            }
            if (!string.IsNullOrEmpty(strContentType) && strContentType.Contains("gzip"))//判断是否gzip压缩
            {
                _responseBodyData = GZipStreamEx.GZipDecompress(_responseBodyData);
            }
            strContentType = null;
            if (_responseHeader.ContainsKey("content-type"))
            {
                strContentType = _responseHeader["content-type"];
            }
            if (_responseBodyEncode == null)//判断是否指定编码
            {
                if (!string.IsNullOrEmpty(strContentType) && strContentType.Contains("="))
                {
                    string[] arrayHeaderInfo = strContentType.Split('=');
                    Encoding encode = Encoding.GetEncoding(arrayHeaderInfo[1]);
                    _responseBodyEncode = encode;
                }
                else
                {
                    _responseBodyEncode = Encoding.Default;
                }
            }
            _responseBody = _responseBodyEncode.GetString(_responseBodyData);
            return _responseBody;
        }

        private static byte[] LinkDataByTransferEncoding(byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            string strCount = string.Empty;
            bool isData = false;
            for (int i = 0; i < data.Length; i++)
            {
                if ((i + 1) >= data.Length) continue;
                if (data[i] == 13 && data[i + 1] == 10)
                {
                    if (isData)
                    {
                        strCount = string.Empty;
                    }
                    else
                    {
                        if (strCount == "0")
                        {
                            break;
                        }
                    }
                    isData = !isData;
                    continue;
                }
                if (i > 0)
                {
                    if (data[i - 1] == 13 && data[i] == 10)
                    {
                        continue;
                    }
                }
                if (!isData)
                {
                    strCount += (char)data[i];
                }
                else
                {
                    ms.Write(new[] { data[i] }, 0, 1);
                }
            }
            byte[] temp = ms.ToArray();
            ms.Close();
            return temp;
        }

        public void Dispose()
        {
            if (_fileStream != null)
            {
                _fileStream.Close();
            }
            if (txtStream != null)
            {
                _responseBodyData = txtStream.ToArray();
                txtStream.Close();
            }
        }

    }
}
